/*-------------------------------------------------------------------------
 *
 * vacuum.h
 *	  postgres 垃圾收集器和统计分析器的头文件
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/commands/vacuum.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef VACUUM_H
#define VACUUM_H

#include "access/htup.h"
#include "access/genam.h"
#include "access/parallel.h"
#include "catalog/pg_class.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "parser/parse_node.h"
#include "storage/buf.h"
#include "storage/lock.h"
#include "utils/relcache.h"

/*
 * amparallelvacuumoptions 的标志，用于控制 bulkdelete
 * 和 vacuumcleanup 在并行 vacuum 中的参与。
 */

/*
 * 默认情况下，bulkdelete 和 vacuumcleanup 都被禁用。
 * 这将被不想参与或无法参与并行
 * vacuum 的 IndexAM 使用。例如，如果一个索引 AM 没有办法将
 * 第一个 ambulkdelete 调用分配的索引统计信息传达给后续
 * 调用，直到 amvacuumcleanup，则索引 AM 无法参与并行
 * vacuum。
 */
#define VACUUM_OPTION_NO_PARALLEL			0

/*
 * bulkdelete 可以并行执行。这个选项可以被
 * 需要扫描索引以删除元组的索引 AM 使用。
 */
#define VACUUM_OPTION_PARALLEL_BULKDEL		(1 << 0)

/*
 * 如果尚未执行 bulkdelete，则 vacuumcleanup 可以并行执行。
 * 这将被可以扫描索引的 IndexAM 使用，
 * 如果尚未执行 bulkdelete。
 */
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP	(1 << 1)

/*
 * 即使 bulkdelete 已经处理过索引，vacuumcleanup 也可以并行执行。
 * 这将被在索引的清理阶段扫描索引的 IndexAM 使用，
 * 无论在 bulkdelete 阶段索引是否已被扫描。
 */
#define VACUUM_OPTION_PARALLEL_CLEANUP		(1 << 2)

/* 检查 vacuum 标志的值 */
#define VACUUM_OPTION_MAX_VALID_VALUE		((1 << 3) - 1)

/* 并行 vacuum 状态的抽象类型 */
typedef struct ParallelVacuumState ParallelVacuumState;

/*----------
 * ANALYZE 为要分析的每个属性（列）构建一个这些结构之一。
 * 该结构和附属数据在 anl_context 中，
 * 因此它们将持续到 ANALYZE 操作结束。
 *
 * 类型特定的 typanalyze 函数被传递一个指向该结构的指针
 * 必须返回 true 以继续分析，返回 false 跳过此列的分析。
 * 在 true 的情况下，必须设置 compute_stats 和 minrows 字段，
 * 并可以选择性地设置 extra_data 以传递额外信息给 compute_stats。
 * minrows 是它请求的收集的最小样本行数
 * （但请注意，这个请求可能不会被满足，例如如果表中行数少于此）。
 *
 * 统计信息计算例程将在收集样本行后被调用。
 * 除了这个结构外，它还会传入：
 *		fetchfunc：一个用于从样本行中访问列值的函数
 *		samplerows：样本元组的数量
 *		totalrows：关系中估计的总行数
 * fetchfunc 可能会被调用，rownum 从 0 到 samplerows-1。
 * 它返回一个 Datum 和一个 isNull 标志。
 *
 * 如果 compute_stats 能够计算任何有用的统计信息，则应将 stats_valid 设置为 true。
 * 如果这样做，结构的其余部分将包含要存储在列的 pg_statistic 行中的信息。
 * 小心在 anl_context 中分配任何指向的数据，这在调用 compute_stats 时将不是
 * CurrentMemoryContext。
 *
 * 注意：用于统计目的的所有比较应使用
 * 基础列的 collation（attcollation），除非在非可排序容器类型包含可排序类型的情况下；
 * 在这种情况下，请使用该类型的默认 collation。确保在 stacoll 中记录
 * 适当的 collation。
 *----------
 */
typedef struct VacAttrStats *VacAttrStatsP;

typedef Datum (*AnalyzeAttrFetchFunc) (VacAttrStatsP stats, int rownum,
									   bool *isNull);

typedef void (*AnalyzeAttrComputeStatsFunc) (VacAttrStatsP stats,
											 AnalyzeAttrFetchFunc fetchfunc,
											 int samplerows,
											 double totalrows);

typedef struct VacAttrStats
{
	/*
	 * 这些字段在调用特定类型的typanalyze函数之前由主ANALYZE代码设置。
	 *
	 * 注意：不要假设被分析的数据具有attr中显示的相同数据类型，
	 * 即不要信任attr->atttypid、attlen等。这是因为某些索引操作类
	 * 存储的类型与基础列/表达式不同。相反，使用attrtypid、attrtypmod
	 * 和attrtype获取传递给typanalyze函数的数据类型的信息。
	 * 同样，使用attrcollid而不是attr->attcollation。
	 */
	Form_pg_attribute attr;		/* 列的pg_attribute行副本 */
	Oid			attrtypid;		/* 被分析的数据类型 */
	int32		attrtypmod;		/* 被分析数据的类型修改 */
	Form_pg_type attrtype;		/* attrtypid 的 pg_type 行的副本 */
	Oid			attrcollid;		/* 被分析数据的排序规则 */
	MemoryContext anl_context;	/* 保存长期数据的位置 */

	/*
	 * 这些字段必须由 typanalyze 例程填写，除非它
	 * 返回 false。
	 */
	AnalyzeAttrComputeStatsFunc compute_stats;	/* 函数指针 */
	int			minrows;		/* 统计所需的最小行数 */
	void	   *extra_data;		/* 额外的特定类型数据 */

	/*
	 * 这些字段由 compute_stats 例程填写。（它们在结构创建时初始化为零。）
	 */
	bool		stats_valid;
	float4		stanullfrac;	/* NULL 条目的比例 */
	int32		stawidth;		/* 列值的平均宽度 */
	float4		stadistinct;	/* # 不同的值 */
	int16		stakind[STATISTIC_NUM_SLOTS];
	Oid			staop[STATISTIC_NUM_SLOTS];
	Oid			stacoll[STATISTIC_NUM_SLOTS];
	int			numnumbers[STATISTIC_NUM_SLOTS];
	float4	   *stanumbers[STATISTIC_NUM_SLOTS];
	int			numvalues[STATISTIC_NUM_SLOTS];
	Datum	   *stavalues[STATISTIC_NUM_SLOTS];

	/*
	 * 这些字段描述 stavalues[n] 元素类型。它们将被
	 * 初始化以匹配 attrtypid，但自定义的 typanalyze 函数可能
	 * 希望存储一个与被分析列元素不同的数组。因此它应该覆盖这些字段。
	 */
	Oid			statypid[STATISTIC_NUM_SLOTS];
	int16		statyplen[STATISTIC_NUM_SLOTS];
	bool		statypbyval[STATISTIC_NUM_SLOTS];
	char		statypalign[STATISTIC_NUM_SLOTS];

	/*
	 * 这些字段是 ANALYZE 主要代码的私有部分，不应由特定类型的功能查看。
	 */
	int			tupattnum;		/* 元组中的属性编号 */
	HeapTuple  *rows;			/* 标准提取函数的访问信息 */
	TupleDesc	tupDesc;
	Datum	   *exprvals;		/* 索引提取函数的访问信息 */
	bool	   *exprnulls;
	int			rowstride;
} VacAttrStats;

/* VacuumParams->options 的标志位 */
#define VACOPT_VACUUM 0x01		/* 执行 VACUUM */
#define VACOPT_ANALYZE 0x02		/* 执行 ANALYZE */
#define VACOPT_VERBOSE 0x04		/* 输出 INFO 监控消息 */
#define VACOPT_FREEZE 0x08		/* FREEZE 选项 */
#define VACOPT_FULL 0x10		/* 完全（非并发）清理 */
#define VACOPT_SKIP_LOCKED 0x20 /* 如果无法获取锁则跳过 */
#define VACOPT_PROCESS_TOAST 0x40	/* 处理 TOAST 表（如果有） */
#define VACOPT_DISABLE_PAGE_SKIPPING 0x80	/* 不跳过任何页面 */

/*
 * index_cleanup 和截断参数使用的值。
 *
 * VACOPTVALUE_UNSPECIFIED 用作初始占位符，当 VACUUM
 * 命令没有明确值时。这样时，最终可用的值
 * 来自相应的 reloption（尽管通常使用 reloption 默认值）。
 */
typedef enum VacOptValue
{
	VACOPTVALUE_UNSPECIFIED = 0,
	VACOPTVALUE_AUTO,
	VACOPTVALUE_DISABLED,
	VACOPTVALUE_ENABLED,
} VacOptValue;

/*
 * 自定义 VACUUM 和 ANALYZE 行为的参数。
 *
 * 请注意，选项中必须至少设置 VACOPT_VACUUM 或 VACOPT_ANALYZE 之一。
 */
typedef struct VacuumParams
{
	bits32		options;		/* VACOPT_* 的位掩码 */
	int			freeze_min_age; /* 最小冻结年龄，-1 使用默认值 */
	int			freeze_table_age;	/* 扫描整张表的年龄 */
	int			multixact_freeze_min_age;	/* 最小的多事务冻结年龄，-1使用默认值 */
	int			multixact_freeze_table_age; /* 扫描整个表的多事务年龄 */
	bool		is_wraparound;	/* 强制进行全面的清理 */
	int			log_min_duration;	/* 以毫秒为单位的最小执行阈值，低于该值的自动清理将被记录，-1使用默认值 */
	VacOptValue index_cleanup;	/* 进行索引清理和清理 */
	VacOptValue truncate;		/* 截断末尾的空页面 */

	/*
	 * 并行清理工作线程的数量。 默认值为0，这意味着根据索引的数量进行选择。
	 * -1表示并行清理已禁用。
	 */
	int			nworkers;
} VacuumParams;

/*
 * VacDeadItems存储通过索引清理删除的索引元组的TIDs。
 */
typedef struct VacDeadItems
{
	int			max_items;		/* 在数组中分配的槽的数量 */
	int			num_items;		/* 当前条目数量 */

	/* 要从索引中删除的TIDs的已排序数组 */
	ItemPointerData items[FLEXIBLE_ARRAY_MEMBER];
} VacDeadItems;

#define MAXDEADITEMS(avail_mem) \
	(((avail_mem) - offsetof(VacDeadItems, items)) / sizeof(ItemPointerData))

/* GUC 参数 */
extern PGDLLIMPORT int default_statistics_target;	/* PostGIS的PGDLLIMPORT */
extern PGDLLIMPORT int vacuum_freeze_min_age;
extern PGDLLIMPORT int vacuum_freeze_table_age;
extern PGDLLIMPORT int vacuum_multixact_freeze_min_age;
extern PGDLLIMPORT int vacuum_multixact_freeze_table_age;
extern PGDLLIMPORT int vacuum_failsafe_age;
extern PGDLLIMPORT int vacuum_multixact_failsafe_age;

/* 基于成本的并行清理的变量 */
extern PGDLLIMPORT pg_atomic_uint32 *VacuumSharedCostBalance;
extern PGDLLIMPORT pg_atomic_uint32 *VacuumActiveNWorkers;
extern PGDLLIMPORT int VacuumCostBalanceLocal;


/* 在commands/vacuum.c中 */
extern void ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel);
extern void vacuum(List *relations, VacuumParams *params,
				   BufferAccessStrategy bstrategy, bool isTopLevel);
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
							 int *nindexes, Relation **Irel);
extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);
extern double vac_estimate_reltuples(Relation relation,
									 BlockNumber total_pages,
									 BlockNumber scanned_pages,
									 double scanned_tuples);
extern void vac_update_relstats(Relation relation,
								BlockNumber num_pages,
								double num_tuples,
								BlockNumber num_all_visible_pages,
								bool hasindex,
								TransactionId frozenxid,
								MultiXactId minmulti,
								bool *frozenxid_updated,
								bool *minmulti_updated,
								bool in_outer_xact);
extern bool vacuum_set_xid_limits(Relation rel,
								  int freeze_min_age, int freeze_table_age,
								  int multixact_freeze_min_age,
								  int multixact_freeze_table_age,
								  TransactionId *oldestXmin,
								  MultiXactId *oldestMxact,
								  TransactionId *freezeLimit,
								  MultiXactId *multiXactCutoff);
extern bool vacuum_xid_failsafe_check(TransactionId relfrozenxid,
									  MultiXactId relminmxid);
extern void vac_update_datfrozenxid(void);
extern void vacuum_delay_point(void);
extern bool vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple,
									 bits32 options);
extern Relation vacuum_open_relation(Oid relid, RangeVar *relation,
									 bits32 options, bool verbose,
									 LOCKMODE lmode);
extern IndexBulkDeleteResult *vac_bulkdel_one_index(IndexVacuumInfo *ivinfo,
													IndexBulkDeleteResult *istat,
													VacDeadItems *dead_items);
extern IndexBulkDeleteResult *vac_cleanup_one_index(IndexVacuumInfo *ivinfo,
													IndexBulkDeleteResult *istat);
extern Size vac_max_items_to_alloc_size(int max_items);

/* 在commands/vacuumparallel.c中 */
extern ParallelVacuumState *parallel_vacuum_init(Relation rel, Relation *indrels,
												 int nindexes, int nrequested_workers,
												 int max_items, int elevel,
												 BufferAccessStrategy bstrategy);
extern void parallel_vacuum_end(ParallelVacuumState *pvs, IndexBulkDeleteResult **istats);
extern VacDeadItems *parallel_vacuum_get_dead_items(ParallelVacuumState *pvs);
extern void parallel_vacuum_bulkdel_all_indexes(ParallelVacuumState *pvs,
												long num_table_tuples,
												int num_index_scans);
extern void parallel_vacuum_cleanup_all_indexes(ParallelVacuumState *pvs,
												long num_table_tuples,
												int num_index_scans,
												bool estimated_count);
extern void parallel_vacuum_main(dsm_segment *seg, shm_toc *toc);

/* 在commands/analyze.c中 */
extern void analyze_rel(Oid relid, RangeVar *relation,
						VacuumParams *params, List *va_cols, bool in_outer_xact,
						BufferAccessStrategy bstrategy);
extern bool std_typanalyze(VacAttrStats *stats);

/* 在utils/misc/sampling.c中 --- utils/sampling.h中的声明的重复 */
extern double anl_random_fract(void);
extern double anl_init_selection_state(int n);
extern double anl_get_next_S(double t, int n, double *stateptr);

#endif							/* VACUUM_H */
